package dkbta.processing.tasks;

import java.util.ArrayList;
import java.util.Collection;

import dkbta.ontology.elements.ElementDef;
import dkbta.processing.WorkQueue;

/**
 * A general task. This class contains the handling of the common aspects of all tasks
 * such as parent-child-task relations, the associated element/patient-id, whether the
 * task is primary. It also provides an <tt>equals</tt> implementation that works for
 * all extending types.
 * 
 * @author Uri Kanonov
 */
public abstract class Task{
	/** The task's associated element */
	public final ElementDef element;

	/** Whether the task is primary (the work queue waits for it to complete) */
	public final boolean isPrimary;

	/** The associated patient id, used for equals comparisons */
	protected final int _patientId;

	/** Cached hashcode */
	private final int _hashCode;

	/** The tasks that depend on this task for completion */
	private Collection<Task> _parentTasks;

	/** The number of tasks this task depends on */
	private int _childTasks;

	/**
	 * Initializes a task
	 * 
	 * @param element The associated element
	 * @param patientId The associated patient-id
	 * @param isPrimary Whether the task is a primary task
	 */
	public Task(ElementDef element, int patientId, boolean isPrimary){
		this.element = element;
		this.isPrimary = isPrimary;
		_patientId = patientId;

		_hashCode = (element.getElementType() + element.getName() + patientId).hashCode();
		_childTasks = 0;
	}

	/**
	 * Adds a parent task to the task. The parent task will be notified upon the
	 * completion of this task.
	 * 
	 * @param parentTask
	 */
	public void addParentTask(Task parentTask){
		if (_parentTasks == null){
			_parentTasks = new ArrayList<Task>();
		}
		_parentTasks.add(parentTask);
	}

	/**
	 * Notes that this task just gained another prerequisite task
	 */
	public void addChildTask(){
		++_childTasks;
	}

	/**
	 * Performs this task (computes the associated element)
	 * 
	 * @param wq A reference to the work queue
	 * @return Whether the task has been completed. It will be true if all prerequisite
	 *         elements for the computation of the associated element have already been
	 *         computed and false otherwise.
	 */
	public abstract boolean performTask(WorkQueue wq);

	/**
	 * Invoked when this task is completed. It notifies all parent tasks (those whose
	 * execution depends on the completion of this task) of the completion.
	 * 
	 * @param wq A reference to the work queue
	 */
	public void notifyParentTasksAboutCompletion(WorkQueue wq){
		if (_parentTasks != null){
			for (Task parentTask : _parentTasks){
				parentTask.childTaskCompleted(wq);
			}
		}
	}

	/**
	 * Invoked when a child task (a task that was created as a prerequisite of this task)
	 * is completed.<br>
	 * If it was the last child task, this task is automatically re-added to the work
	 * queue
	 * 
	 * @param wq A reference to the work queue
	 */
	public void childTaskCompleted(WorkQueue wq){
		--_childTasks;
		if (_childTasks == 0){
			wq.addTask(this);
		}
	}

	/**
	 * This task is equal only to another task of the same runtime class with the same
	 * patient id and associated element
	 * 
	 * @param obj The object to be compared with
	 * @return Whether this task is equal to the given object
	 */
	@Override
	public boolean equals(Object obj){
		if (obj != null && obj.getClass() == getClass()){
			Task t = (Task)obj;
			return _patientId == t._patientId && element.equals(t.element);
		}
		return false;
	}

	@Override
	public int hashCode(){
		return _hashCode;
	}

}
